/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		acleditor command.cpp
 * PURPOSE:		acl dialog user command processing
 *				included as part of acleditor.cpp
 */

class AclEditorCommand : public AclEditorSave
{
protected:
	AclEditorCommand(AclEditorInitParams const &Init) :
		AclEditorSave(Init)
	{}

private:
	ColumnGroups ToolButtonShowToColumnGroup(ToolButtonIndex Tbi)
	{
		if(Tbi < TbShowSid || Tbi > TbShowExtra)
			RaiseTextError(L"Tool button index outside column group button range");

		return c_cast<ColumnGroups>(Tbi - TbShowSid + CgTrusteeSid);
	}

	void CheckOneToolButtonInGroup(int CmdStart, int CmdEnd, int CmdOn)
	{
		for(int i = CmdStart; i <= CmdEnd; i++)
			RaiseOnWin32ErrorT(SendMessage(Toolbar, TB_CHECKBUTTON, i, MAKELONG(FALSE, 0)));

		RaiseOnWin32ErrorT(SendMessage(Toolbar, TB_CHECKBUTTON, CmdOn, MAKELONG(TRUE, 0)));
	}

	void ToggleToolButtonCheckState(int Cmd)
	{
		RaiseOnWin32ErrorT(SendMessage(Toolbar, TB_CHECKBUTTON, Cmd, MAKELONG(!SendMessage(Toolbar, TB_ISBUTTONCHECKED, Cmd, 0), 0)));
	}

	void Reinherit()
	{
		DN_QUERY_INHERITANCE_ACL DialogCb;

		SaveAcl(DialogCb.AclAndState);

		Callback.SendCallback(DN_QUERY_INHERITANCE, &DialogCb);

		ClearRows();

		LoadAcl(DialogCb.AclAndState);
	}

	void LoadDefault()
	{
		DN_QUERY_DEFAULT_ACL DialogCb;

		SaveAcl(DialogCb.AclAndState);

		Callback.SendCallback(DN_QUERY_DEFAULT, &DialogCb);

		ClearRows();

		LoadAcl(DialogCb.AclAndState);
	}

	void AcceleratorCommand(int Cmd)
	{
		//updates toolbar state accordingly, and passes the command to ToolbarCommand
		//except ID_EDIT_ROW to open a text popup

		switch(Cmd)
		{
		case ID_EDIT_ROW:
			ShowTextEditorForColumn(static_cast<UCHAR>(GetWindowLong(GetFocus(), GWL_ID)));
			return;

		case TbShowSpecific:
		case TbShowStandard:
		case TbShowGeneric:
		case TbShowExtra:
		case TbShowInheritSource:
		case TbShowSid:
		case TbShowObject:
		case TbShowObjectInherit:
		case TbDefaulted:
		case TbProtected:
		case TbAutoInherit:
		case TbUntrusted:
		case TbMergeUnprotect:
			ToggleToolButtonCheckState(Cmd);
			break;

		case TbMergeNone:
		case TbMergeInhNone:
		case TbMergeInhLast:
		case TbMergeInhFirst:
		case TbMergeInhOnly:
		case TbMergeDenyFirst:
			CheckOneToolButtonInGroup(TbMergeStart, TbMergeEnd, Cmd);
			break;
		}

		ToolbarCommand(static_cast<ToolButtonIndex>(Cmd));
	}

	void ToolbarCommand(ToolButtonIndex Cmd)
	{
		switch(Cmd)
		{
		case TbOk:
		case TbApply:
			{
				ACL_AND_STATE AclAndState;
				SaveAcl(AclAndState);
				
				Callback.SendCallback(Cmd == TbOk ? DN_CLOSE_OK : DN_APPLY, &AclAndState);

				if(Cmd == TbOk)
					PostMessage(Wnd, WM_SYSCOMMAND, SC_CLOSE, 0);
			}
			break;

		case TbCancel:
			PostMessage(Wnd, WM_SYSCOMMAND, SC_CLOSE, 0);
			break;

		case TbReturnNull:
		case TbReturnUnspecified:
			{
				ACL_AND_STATE AclAndState;

				AclAndState.Acl = NULL;

				if(Cmd == TbReturnNull)
					AclAndState.State = ACL_FLAG_CHANGED | ACL_FLAG_PRESENT;
				else //Cmd == TbReturnUnspecified
					AclAndState.State = 0;

				Callback.SendCallback(DN_CLOSE_OK, &AclAndState);
				PostMessage(Wnd, WM_SYSCOMMAND, SC_CLOSE, 0);
			}
			break;

		case TbShowExtra:
			ShowExtras = static_cast<BOOLEAN>(SendMessage(Toolbar, TB_ISBUTTONCHECKED, Cmd, NULL));
			//[continue]
		case TbShowSpecific:
		case TbShowStandard:
		case TbShowGeneric:
		case TbShowSid:
		case TbShowObject:
		case TbShowObjectInherit:
		case TbShowInheritSource:
			if(SetColumnGroupVisibibility(ToolButtonShowToColumnGroup(Cmd), static_cast<BOOL>(SendMessage(Toolbar, TB_ISBUTTONCHECKED, Cmd, NULL))))
				RefreshColumnSizes();

			break;

		case TbInsertBefore:
			InsertNewRow(FALSE);
			break;

		case TbInsertAfter:
			InsertNewRow(TRUE);
			break;

		case TbShiftUp:
			SwapRows(TRUE);
			break;

		case TbShiftDown:
			SwapRows(FALSE);
			break;

		case TbDelete:
			DeleteRow();
			break;

		case TbClear:
			ClearRows();
			break;

		case TbReinherit:
			Reinherit();
			break;

		case TbLoadDefault:
			LoadDefault();
			break;
		}
	}

	void ShowMenuEditorForColumn(UCHAR Col, HMENU Menu, int x, int y, PCWSTR Lookup[])
	{
		//simulate a click to ensure the correct item is selected, even if command originated from the keyboard
		POINT pt;
		pt.x = x;
		pt.y = y;

		ScreenToClient(Columns[Col].Wnd, &pt);

		SendMessage(Columns[Col].Wnd, WM_LBUTTONDOWN, 0, MAKELPARAM(pt.x, pt.y));
		SendMessage(Columns[Col].Wnd, WM_LBUTTONUP, 0, MAKELPARAM(pt.x, pt.y));

		BOOL Selection = TrackPopupMenu(Menu, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RETURNCMD, x, y, 0, Wnd, NULL);
		
		int rowindex = GetSelectedRowIndex(Col);

		if(Selection)
			SetListBoxItemText(Columns[Col].Wnd, rowindex, Lookup[Selection - 1]);
	}

	void ShowMenuEditorForColumn(HWND SrcWnd, HMENU Menu, DWORD Position, PCWSTR Lookup[])
	{
		UCHAR col = GetColumnIndexFromWnd(SrcWnd);

		ShowMenuEditorForColumn(col, Menu, LOWORD(Position), HIWORD(Position), Lookup);
	}

	virtual HACCEL GetAcceleratorHandle() override
	{
		return Accelerator;
	}

	HMENU GetAceTypeMenu()
	{
		return ShowExtras ? MenuAceTypeEx : (SdState & SD_FLAG_OBJECT_SUPPORT ? MenuAceTypeObj : MenuAceTypeStd);
	}

	virtual LRESULT RecieveMessage(HWND wnd, UINT msg, WPARAM wparam, LPARAM lparam) override
	{
		switch(msg)
		{
		case WM_DESTROY:
			Callback.SendCallback(DN_CLOSE_CANCEL, NULL);
			return TRUE;

		case WM_GETDLGCODE:
			return DLGC_WANTARROWS;

		case WM_COMMAND:
			if(lparam == 0 && HIWORD(wparam) == 1) //lparam == 0 && HIWORD(wparam) == 1 means it's from an accelerator
				AcceleratorCommand(LOWORD(wparam));
			else if(c_cast<HWND>(lparam) == Toolbar)
				ToolbarCommand(static_cast<ToolButtonIndex>(LOWORD(wparam)));
			else //command msg from a listbox
				switch(HIWORD(wparam))
				{
				case LBN_SELCHANGE:
					if(!(GetWindowLong(Columns[LOWORD(wparam)].Wnd, GWL_STYLE) & LBS_MULTIPLESEL)) //if not multiplesel, a row selecting column
					{
						int item = (int)SendMessage(Columns[LOWORD(wparam)].Wnd, LB_GETCURSEL, NULL, NULL);

						if(LOWORD(wparam) >= COL_MIN_ACE && LOWORD(wparam) <= COL_MAX_ACE_SEL)
							SyncAceSelection(item);
					}
				break;

			case LBN_DBLCLK:
				switch(LOWORD(wparam)) //LOWORD(wparam) is the column number
				{
				case COL_TSTE_NAME:
				case COL_TSTE_SID:
				case COL_OBJ_TYPE:
				case COL_OBJ_INH_TYPE:
					ShowTextEditorForColumn(static_cast<UCHAR>(LOWORD(wparam)));
					break;

				case COL_ACE_TYPE:
					ShowMenuEditorForColumn(reinterpret_cast<HWND>(lparam), GetAceTypeMenu(), GetMessagePos(), AceTypeLabels);
					break;
				}
			}
			break;

		case WM_NOTIFY:
			{
				LPNMHDR NotifyHdr = c_cast<LPNMHDR>(lparam);
				switch(NotifyHdr->code)
				{
				case TTN_GETDISPINFO:
					{
						TOOLTIPTEXT *ToolText = c_cast<TOOLTIPTEXT *>(lparam);
						TBBUTTON Button;

						LRESULT CmdIndex = SendMessage(Toolbar, TB_COMMANDTOINDEX, ToolText->hdr.idFrom, NULL);
						
						RaiseOnWin32ErrorT(SendMessage(Toolbar, TB_GETBUTTON, CmdIndex, c_cast<LPARAM>(&Button)));

						ToolText->lpszText = c_cast<PWSTR>(Button.dwData);
					}
				}
				break;
			}
			break;

		case WM_CONTEXTMENU:
			{
				UCHAR col = GetColumnIndexFromWnd((HWND)wparam);

				if(col == MAXUCHAR || RowMax == MAXUSHORT)
					break;

				{
					//simulate a click to ensure the correct item is selected, because WM_CONTEXTMENU is processed instead of click normally
					LPARAM pos = GetMessagePos();
					POINTS pts = MAKEPOINTS(pos);
					POINT pt = {pts.x, pts.y};

					if(pt.x != -1 || pt.y != -1)
					{	
						ScreenToClient(Columns[col].Wnd, &pt);

						SendMessage(Columns[col].Wnd, WM_LBUTTONDOWN, 0, MAKELPARAM(pt.x, pt.y));
						SendMessage(Columns[col].Wnd, WM_LBUTTONUP, 0, MAKELPARAM(pt.x, pt.y));
					}
				}

				RECT itemrect = GetListBoxItemScreenRect((HWND)wparam, GetSelectedRowIndex(col));

				switch(col)
				{
				case COL_ACE_TYPE:
					ShowMenuEditorForColumn(col, GetAceTypeMenu(), itemrect.left, itemrect.bottom, AceTypeLabels);
					break;

				case COL_TSTE_NAME:
				case COL_TSTE_SID:
				case COL_OBJ_TYPE:
				case COL_OBJ_INH_TYPE:
					ShowEnumMenuForColumn(col, itemrect.left, itemrect.bottom);
					break;
				}
			}
			break;
		}

		return __super::RecieveMessage(wnd, msg, wparam, lparam);
	}
};
